
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:im_app/im/page/view/model/DataChangeListener.dart';
import 'package:im_app/im/service/ImMessageService.dart';
import 'package:im_app/im/util/DbUtil.dart';
import 'package:im_app/im/util/ImCacheFactory.dart';
import 'package:im_app/im/util/MessageUtil.dart';
import 'package:logger/logger.dart';

import '../../dao/ImGroupDao.dart';
import '../../dao/ImMessageDao.dart';
import '../../dao/ImSessionDao.dart';
import '../../pojo/ImPojo.dart';
import '../../service/ImMessageSendService.dart';
import '../../socket/dto/business/SendData.dart';
import '../../socket/service/RpcSendDataService.dart';
import '../../util/ImConstant.dart';
import '../../util/ImSingleton.dart';
import 'MessageListModel.dart';

typedef StateChangeFunction = Function(List<ImMessage> imMessages);

class MessageSendListModel extends ChangeNotifier {

  final _logger = Logger(
    printer: PrettyPrinter(),
  );

  final int sessionId;
  final DataChangeStateListener<StatefulWidget, ImMessage> messageDataChangeListener;
  final DateTime? startDay;
  late MessageListModel _messageListModel;

  MessageSendListModel(this.messageDataChangeListener, this.sessionId, this.startDay) {
    _messageListModel = MessageListModel(messageDataChangeListener: messageDataChangeListener, sessionId: sessionId) ;
  }

  String _errorMessage = "";
  bool _showEmoji = false;

  String get sendMessageForError {
    return _errorMessage;
  }

  bool get showEmoji => _showEmoji;

  void switchEmoji() {
    _showEmoji = !_showEmoji;
    notifyListeners();
  }

  void hideEmoji() {
    if (_showEmoji) {
      _showEmoji = false;
      notifyListeners();
    }
  }

  void clearMessages() {
    messageDataChangeListener.clearDatas();
    notifyListeners();
  }

  bool canShowMessage(ImMessage message) {
    return this.sessionId == message.sessionId;
  }

  void addMessage(ImMessage imMessage) {
    if (!canShowMessage(imMessage)) return;
    messageDataChangeListener.addFirst(imMessage);
    notifyListeners();
  }

  void updateMessage(ImMessage imMessage) {
    if (!canShowMessage(imMessage)) return;
    messageDataChangeListener.updateData(imMessage);
    notifyListeners();
  }

  void updateMessages(List<ImMessage> imMessages) {
    for (var message in imMessages) {
      if (!canShowMessage(message)) continue;
      messageDataChangeListener.updateData(message);
    }
    notifyListeners();
  }

  void addMessages(List<ImMessage> imMessages) {
    List<ImMessage> canAddMessages = imMessages.where(canShowMessage).toList();
    if (canAddMessages.isEmpty) return;
    messageDataChangeListener.addFirsts(canAddMessages);
    notifyListeners();
  }

  void deleteMessage(ImMessage imMessage) {
    if (!canShowMessage(imMessage)) return;
    messageDataChangeListener.deleteData(imMessage);
    notifyListeners();
  }

  Future<List<ImMessage>> loadData() async {
    _logger.i("$sessionId begin load data!");

    var messageService = ImMessageService();
    List<ImMessage> result = await messageService.getNewsBySessionId(sessionId, startDay, ImConstant
        .MESSAGE_LIST_DEFAULT_SIZE);

    if(result.isNotEmpty) {
      _sortImMessages(result);        // 排序
      messageDataChangeListener.addDatas(result);

      await _sendReadMessages(result);
      await _reduceHotMessageTotal();// 发送未读消息事件
    }

    return result;
  }

  Future<List<ImMessage>> loadNewsData() async {
    return _messageListModel.loadNewsData();
  }

  Future<List<ImMessage>> loadOldData() async {
    return _messageListModel.loadOldData();
  }

  Future<void> sendMessage(String content, ImMessageType messageType) async {
    var sessionDao = ImSessionDao();
    var messageSendService = ImMessageSendService();

    ImSession? session = await sessionDao.getById(sessionId);
    var imMessage = await messageSendService.buildMessage(session!, content, messageType, null);
    session?.latelyMessage = MessageUtil.buildLastedMessage(messageType, content);

    messageDataChangeListener.addFirst(imMessage);

    ImSingleton.getInstance().sessionListCubit?.changeSession(session);

    await messageSendService.sendMessage([session], [imMessage]);
  }

  List<ImMessage> _filterNeedHandleMessages(String userId, List<ImMessage> messages) {
    List<ImMessage> needHandleMessages = [];    // 改变状态的为已读的消息
    for (ImMessage message in messages) {
      if (message.isSender(userId)) continue;
      if (message.actionStatus != ImMessageActionStatus.none && message.actionStatus != null && message.actionStatus
          != ImMessageActionStatus.read) continue;
      needHandleMessages.add(message);
    }
    return needHandleMessages;
  }

  Future<void> _sendReadMessages(List<ImMessage> messages) async {
    RpcMessageService rpcMessageService = new RpcMessageService();
    String userId = ImSingleton.getInstance().session.userId!;
    List<ImMessage> sendReadMessages = _filterNeedHandleMessages(userId, messages);
    if (sendReadMessages.isNotEmpty) {
      List<String> serverIds = sendReadMessages.where((m) => m.serverId != null).map((m) => m.serverId!).toList();
      var messageReadReq = MessageReadReq(messageIds: serverIds);
      await rpcMessageService.sendReadMessages(messageReadReq);
      sendReadMessages.forEach((m) {
        m.actionStatus = ImMessageActionStatus.readed;
        m.updateTime = DateTime.now();
      });

      var messageDao = ImMessageDao();
      await messageDao.batchChangeMessageStatus(sendReadMessages);
    }
  }

  Future<void> _reduceHotMessageTotal() async {
    var sessionDao = ImSessionDao();
    sessionDao.cleanHotMessageNum(sessionId);
    return Future.value(null);
  }

  void _sortImMessages(List<ImMessage> messages) {
    if (messages.isEmpty) return;
    messages.sort((a, b) {
      final DateTime? s1 = a.serverReceiveTime;
      final DateTime? s2 = b.serverReceiveTime;
      if (s1 == null && s2 == null) return 0;
      if (s1 == null && s2 != null) return 1;
      if (s1 != null && s2 == null) return -1;
      if (s1 != null && s2 != null) {
        var t = s2.compareTo(s1);
        if (t == 0) {
          return (b.serverId??'').compareTo((a.serverId??''));
        }
        return t;
      }
      return 0;
    });
  }
}